/* 
 * Gaia Ajax Widgets, an Ajax Widget Library for ASP.NET 2.0
 * Copyright (C) 2007  Frost Innovation AS
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as 
 * published by the Free Software Foundation.
 * 
 * Or if you have purchased the "Professional Edition" from Frost Innovation
 * it is distributed to you in person under the Gaia Commercial License
 * which makes it possible for you to create closed source applications
 * and still be able to distribute those applications without creating 
 * restrictions for your source code.
 * 
 * Unless you're developing GPL software you should and probably legally
 * have to purchase a license of the Gaia Commercial License since otherwise
 * you might be forced to GPL license your derived works.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should find a copy of both the GPL version 2 and the 
 * Gaia Commercial License on disc where you have extracted these files.
 * If not visit http://www.gnu.org for the GPL version or 
 * http://ajaxwidgets.com for the Gaia Commercial License.
 * 
 * Breaking the terms of whatever license you're entitled to use will cause
 * prosecution by Frost Innovation AS. If possible we will demand to settle any 
 * law suits at a court in Norway.
 * 
 */

using System;
using System.ComponentModel;
using System.Web.UI;
using ASP = System.Web.UI.WebControls;
using System.Drawing;
using System.Web.UI.WebControls;
using System.Text;

namespace Gaia.WebWidgets
{
    /// <summary>
    /// A Gaia Ajax Widget DropDownList, acts like a normal ASP.NET Control though instead of generating a PostBack it uses 
    /// an Ajax event handler.
    /// </summary>
    [ToolboxBitmap(typeof(RichEdit), "Resources.Gaia.WebWidgets.RichEdit.bmp")]
    [ParseChildren(true, "Text")]
    [Themeable(true)]
    public class RichEdit : GaiaControl, IExtraPropertyCallbackRenderer, IAttributeAccessor, IAspectableControl
    {
        #region [ -- RichEdit Members -- ]

        private string _text;
        private AspectList _aspects;
        private bool hasSetFocus = false;
        private bool _hasSetEditBold;
        private bool _hasSetEditUnderline;
        private bool _hasSetEditItalic;
        private bool _hasSetEditStrikeThrough;
        private bool _hasSetEditHeader1;
        private bool _hasSetEditHeader2;
        private bool _hasSetEditHeader3; 
        
        #endregion

        #region [ -- RichEdit properties (and setter methods) -- ]

        /// <summary>
        /// Gets or sets the CSS file for the RichEdit control. Every rich edit may be linked
        /// to it's own CSS file which may be totally unrelated to any other CSS file(s) you have 
        /// in the rest of you page. This is conveniant to render custom "editing styles" for your RichEdit while
        /// still preserving the clean interface in the rest of your application.
        /// </summary>
        [AjaxSerializable("setCssFile")]
        [Description("CSS File to associate with RichEdit control.")]
        [Category("RichEdit Custom properties")]
        [DefaultValue("")]
        public string CssFile
        {
            get
            {
                if (ViewState["CssFile"] == null)
                    return "";
                return (string)ViewState["CssFile"];
            }
            set
            {
                ViewState["CssFile"] = value;
            }
        }

        /// <summary>
        /// The Width property of your RichEdit in Units
        /// </summary>
        [AjaxSerializable("setWidth")]
        [Description("Width of Control")]
        [Category("RichEdit Custom properties")]
        [DefaultValue(100)]
        public Unit Width
        {
            get
            {
                if (ViewState["Width"] == null)
                    return new Unit(100);
                return (Unit)ViewState["Width"];
            }
            set
            {
                ViewState["Width"] = value;
            }
        }

        /// <summary>
        /// The Height property of your RichEdit in Units
        /// </summary>
        [AjaxSerializable("setHeight")]
        [Description("Height of Control")]
        [Category("RichEdit Custom properties")]
        [DefaultValue(100)]
        public Unit Height
        {
            get
            {
                if (ViewState["Height"] == null)
                    return new Unit(100);
                return (Unit)ViewState["Height"];
            }
            set
            {
                ViewState["Height"] = value;
            }
        }

        
        /// <summary>
        /// Sets the current selection of the RichEdit to BOLD letters
        /// </summary>
        public void EditBold()
        {
            _hasSetEditBold = true;
        }

        
        /// <summary>
        /// Sets the current selection of the RichEdit to italic (cursive) letters
        /// </summary>
        public void EditItalic()
        {
            _hasSetEditItalic = true;
        }



        /// <summary>
        /// Sets the current selection of the RichEdit to underlined letters
        /// </summary>
        public void EditUnderline()
        {
            _hasSetEditUnderline = true;
        }

        
        /// <summary>
        /// Sets the current selection of the RichEdit to strikethrough letters
        /// </summary>
        public void EditStrikeThrough()
        {
            _hasSetEditStrikeThrough = true;
        }

        
        /// <summary>
        /// Sets the current selection of the RichEdit to header 1
        /// </summary>
        public void EditHeader1()
        {
            _hasSetEditHeader1 = true;
        }



        /// <summary>
        /// Sets the current selection of the RichEdit to header 2
        /// </summary>
        public void EditHeader2()
        {
            _hasSetEditHeader2 = true;
        }

        
        /// <summary>
        /// Sets the current selection of the RichEdit to header 3
        /// </summary>
        public void EditHeader3()
        {
            _hasSetEditHeader3 = true;
        }

        
        /// <summary>
        /// Sets the current selection of the RichEdit to header 3
        /// </summary>
        public void DeleteSelection()
        {
            _hasDeleted = true;
        }
        private bool _hasDeleted;
        private string _pasteHtml;

        /// <summary>
        /// Paste HTML into the currently selected text (or at the caret's position) in the RichEdit Control
        /// </summary>
        /// <param name="html">HTML Content to paste into RichEdit</param>
        public void PasteHtml(string html)
        {
            _pasteHtml = html;
        }

        
        /// <summary>
        /// Gets or sets the text of the rich edit. This is the value of the rich edit. Or the complete text
        /// property of the rich edit.
        /// </summary>
        [AjaxSerializable("setText")]
        [DefaultValue("")]
        [Category("RichEdit custom properties")]
        [Description("Text HTML of Control")]
        [PersistenceMode(PersistenceMode.InnerDefaultProperty)]
        public string Text
        {
            // Special one, doesn't use or need ViewState since it's serialized EVERY REQUEST as a value of 
            // the control due to not wanting to callback the server every time a users clicks any button... ;)
            get
            {
                if (_text == null)
                {
                    if (!Manager.Instance.Page.IsPostBack)
                    {
                        _text = "";
                    }
                    else
                    {
                        _text = Manager.Instance.Page.Request.Params[this.ClientID + "_VALUE"];
                        if (_text == null)
                        {
                            _text = "";
                        }
                        else
                        {
                            // Note we're also replacing angle brackes due to wanting to use this
                            // control ALSO on pages that have ValidateRequest == TRUE...!!
                            _text = _text.Replace("{||", "<").Replace("||}", ">").Replace("{n|n|", "\r\n");
                        }
                    }
                }
                return _text;
            }
            set
            {
                _text = value;
            }
        }

        /// <summary>
        /// Gets the currently selected (if any) text from the RichEdit
        /// </summary>
        public string SelectedText
        {
            get
            {
                if (!DesignMode)
                {
                    if (Manager.Instance.IsAjaxCallback)
                    {
                        return Manager.Instance.Page.Request.Params[this.ClientID + "_SELECTED"];
                    }
                }
                return "";
            }
        }
        

        /// <summary>
        /// Returns the Aspects acossiated with the Control
        /// </summary>
        public AspectList Aspects
        {
            get
            {
                if (_aspects == null)
                    _aspects = new AspectList(this);
                return _aspects;
            }
        }

        #endregion

        #region [ -- Overridet base class methods and constructor -- ]

        /// <summary>
        /// The last step where we can manipulate the rendering of the control(s)/page before rendering
        /// occurs. Gaia uses this override to inject the inclusion of the necessary JavaScript files for
        /// the specific Controls.
        /// </summary>
        /// <param name="e">EventArgs passed on from the system</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (Visible)
            {
                // Our stuff
                Manager.Instance.AddInclusionOfFileFromResource("Gaia.WebWidgets.Scripts.RichEdit.js", typeof(Manager), "Gaia.RichEdit.browserFinishedLoading");

                // Rendering JavaScript inclusion files for Aspects
                Manager.AddInclusionOfAspectScripts(Aspects);
            }
        }

        /// <summary>
        /// In Gaia we use the Control.OnInit method to register the page into the Manager in order
        /// to later be able to manipulate it's rendering and send Ajax data back to the client instead
        /// of the default full rendering HTML.
        /// </summary>
        /// <param name="e">The EventArgs passed on from the System</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (DesignMode)
                return;

            if (Manager.Instance.Page.IsPostBack)
            {
                if (Manager.Instance.Page.Request.Params[this.ClientID + "_VALUE"] != null)
                {
                    _text = Manager.Instance.Page.Request.Params[this.ClientID + "_VALUE"];
                    _text = _text.Replace("{||", "<").Replace("||}", ">").Replace("{n|n|", "\r\n");
                }
            }
        }

        /// <summary>
        /// Since the OnLoad is the first place in ASP.NET that the de-serializing of the ViewState and 
        /// ControlState is finished this is the first place we in Gaia can instantiate our 
        /// PropertyStateManager(Web)Control object associated with our Widget
        /// </summary>
        /// <param name="e">EventArgs passed on from the System</param>
        protected override void OnLoad(EventArgs e)
        {
            StateManager = new PropertyStateManagerControl(this, ClientID, this);
            
            base.OnLoad(e);

        }

        /// <summary>
        /// Sets input focus to a control
        /// </summary>
        [AjaxSerializable("setFocus")]
        public override void Focus()
        {
            if (StateManager != null)
                StateManager.Focus = true;
            hasSetFocus = true;
            base.Focus();
        }


        /// <summary>
        /// Renders the Control's HTML, override this method to add up your own HTML
        /// </summary>
        /// <param name="writer">StreamWriter to render Control into</param>
        protected override void RenderControlHtml(HtmlTextWriter writer)
        {
            string html = string.Format("<iframe frameborder=\"0\" id=\"{0}\" style=\"width:{1};height:{2}\"></iframe>", ClientID, Width, Height);
            writer.Write(html);
        }

        /// <summary>
        /// This virtual function should be overrided to provide the custom javascript registration on the client..
        /// </summary>
        /// <returns>The script used to register the control on the client</returns>
        protected override string GetObjectScript()
        {
            // Retrieving aspects registration
            StringBuilder builder = new StringBuilder();

            // Rendering Aspects
            Manager.RenderAspects(builder, Aspects);

            string code = string.Format("Gaia.Control.registerControl(new Gaia.RichEdit('{0}', {{text:'{1}'{2}{3}{4}}}))",
                ClientID,
                HtmlFormatter.FormatHtmlForInnerHTML(Text),
                (CssFile != null && CssFile != string.Empty ? ", cssFile: '" + CssFile + "'" : ""),
                (builder.Length > 0 ? "," + builder : ""),
                (hasSetFocus ? ", hasSetFocus:true" : ", hasSetFocus:false"));
            code += ";";
            return code;
        }

        #endregion

        #region IExtraPropertyCallbackRenderer Members

        void IExtraPropertyCallbackRenderer.InjectPropertyChangesToCallbackResponse(StringBuilder code)
        {
            if (_hasSetEditBold)
                code.Append(".execCommand('Bold')");

            if (_hasSetEditItalic)
                code.Append(".execCommand('Italic')");

            if (_hasSetEditUnderline)
                code.Append(".execCommand('Underline')");

            if (_hasSetEditStrikeThrough)
                code.Append(".execCommand('StrikeThrough')");

            if (_hasSetEditHeader1)
                code.Append(".execCommand('formatBlock', false, '<h1>')");

            if (_hasSetEditHeader2)
                code.Append(".execCommand('formatBlock', false, '<h2>')");

            if (_hasSetEditHeader3)
                code.Append(".execCommand('formatBlock', false, '<h3>')");

            if (_hasDeleted)
                code.Append(".execCommand('Delete')");

            if (_pasteHtml != null)
                code.Append(string.Format(".pasteHtml('{0}')", HtmlFormatter.FormatHtmlForInnerHTML(_pasteHtml)));
        }

        #endregion

        #region IAttributeAccessor Members

        /// <summary>
        /// Get value of the given property
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string GetAttribute(string key)
        {
            object retVal = this.GetType().GetProperty(key).GetGetMethod().Invoke(this, null);
            if (retVal != null)
                return retVal.ToString();
            return "";
        }

        /// <summary>
        /// Sets the value of the given property
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetAttribute(string key, string value)
        {
            this.GetType().GetProperty(key).GetSetMethod().Invoke(this, new object[] { value });
        }

        #endregion
    }
}
